// cs315-pong: Cutting edge simulated drinking game
//
// Authors: Evan Arnold, David Greene, Tucker Shouse

using namespace std;
#include "stglew.h"

#include <math.h>
#include <iostream>
#include <vector>
#include "cs315.h"
#include "mesh.h"
#include "tga.h"
#include "PongGame.h"

#include <STShaderProgram.h>

STShaderProgram *texphongShader;
const char *texphongVertexShaderFilename = "phong_vertex_texture.glsl";
const char *texphongFragmentShaderFilename = "phong_fragment_texture.glsl";

STShaderProgram *phongShader;
const char *phongVertexShaderFilename = "phong_vertex_barebones.glsl";
const char *phongFragmentShaderFilename = "phong_fragment_barebones.glsl";
bool texphong_shading_enabled = false;
bool phong_shading_enabled = false;

void PongGame::initializePongGame(void) {
	anim[0]=0;

	msgTime=0;
	
	status[0]=0;  // reset shot count
	status[1]=0;  // reset hitcount
	status[2]=0;  // 0 = sawin turn, 1 = akers turn
	status[3]=0;  // surroundings rotation on/off
	status[4]=0;  // last shot miss = 0, in = 1


	//akers
	miss[0]= "Good effort!";
	miss[1]= "So close!";
	miss[2]= "Whoops!";
	miss[3]= "Close, just a little off on the aim is all";
	miss[4]= "oh darn!";

	hit[0]= "That went really well, I think.";
	hit[1]= "Nice shot, really well done.";
	hit[2]= "Good job!";
	hit[3]= "Perfect!";
	hit[4]= "You get a papaya of accuracy for that one!";


	//sawin
	miss[5]= "Pathetic!";
	miss[6]= "Weak!";
	miss[7]= "Failure!";
	miss[8]= "You're lucky you're not one of my students...";
	miss[9]= "HAHAHAHAHAHA *breath* AHAHAHAHAHAHA";

	hit[5]= "Lucky shot!";
	hit[6]= "Had to happen eventually";
	hit[7]= "FINALLY";
	hit[8]= "Even when you score, you throw like a baby";
	hit[9]= "I'll remember this...";

	curMsg ="";


	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
	//for transparency (set alpha to < 1)
	glEnable (GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//init the table size
	tablex = 2;
	tablez = 8;
	//init cup size
	cup_h  = 0.375;
	cup_br = 0.1042;
	cup_tr = 0.1667;
	//init ball size
	ball_r = 0.0574;

	//initiate idle/throw values
	phase = 1;	
	last_idle=0.0;
	power=0.0;
	upAng=0.0;
	hAng=0.0;
	lastCupHit = 0;

	//Start the ball somewhere
	ballpos[0] = 0;
	ballpos[1] = 1.5;
	ballpos[2] = 4;
	//Ball ball = Ball();
	quadric = gluNewQuadric();			

	//initiate some viewing values
	rHold=false;
	lHold=false;
	lLastX = 0;
	lLastY = 0;
	viewY=0;
	viewX=0;

	//setting the cups
	for(int i = 0; i < 10; i++){
		cups.push_back(true);
		player_cups.push_back(true);
		cup_pos.push_back(vector<double>());
		cup_pos[i].push_back(0.0);
		cup_pos[i].push_back(0.0);
	}


	int tri = 1;
	cup_pos[0][0] = 0;
	cup_pos[0][1] = -3;
	//int tmp[2] = {0,-3.0};				//temp position for recording cup placement
	for(int i = 0; i < cups.size(); i++){
			if(i+1 == (tri * (tri + 1))/2){
				if(i+1 < cups.size()){
					//If we've made a triangle, move down a row
					cup_pos[i+1][0] = cup_pos[i][0] + (-cup_tr - ((tri-1) * (cup_tr * 2)));
					cup_pos[i+1][1] = cup_pos[i][1] - (cup_tr * sqrt((double)3));
					tri += 1;
				}
			} else {
				//Otherwise, just draw the next cup next to us
				if(i+1 < cups.size()){
					cup_pos[i+1][0] = cup_pos[i][0] + cup_tr * 2;
					cup_pos[i+1][1] = cup_pos[i][1];
				}
		}
	}

	//Load a texture
	glGenTextures(1, &table_tex);
	Tga::loadTGA ("Testex.tga", table_tex, 1);
	glGenTextures(1, &floor_tex);
	Tga::loadTGA ("Floortex.tga", floor_tex, 1);
	glGenTextures(1, &sawin_tex);
	Tga::loadTGA ("jsawin.tga", sawin_tex, 1);
	glGenTextures(1, &akers_tex);
	Tga::loadTGA ("dakers.tga", akers_tex, 1);
	loadModel("horse2.obj");

	CUP_DL = glGenLists(1);
	glNewList(CUP_DL, GL_COMPILE);
		glPushMatrix();
			glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
			glDisable(GL_CULL_FACE);
			setGLMaterial(MAT_RED);
			setInnerGLMaterial(MAT_WHITE);
			glRotatef(-90,1.0,0.0,0.0);
			gluCylinder(quadric,cup_br,cup_tr,cup_h,16,1);
			glPushMatrix();
				setGLMaterial(MAT_BEER);
				setInnerGLMaterial(MAT_BEER);
				glTranslatef(0,0,(2*cup_h/3));
				gluDisk(quadric,0,(cup_tr+cup_br)/(5/2),16,1);
			glPopMatrix();
			setGLMaterial(MAT_RED);
			setInnerGLMaterial(MAT_WHITE);
			glRotatef(-180,1.0,0.0,0.0);
			glTranslatef(0.0,0.0,-0.001);
			gluDisk(quadric,0.0,cup_br,16,1);
			glEnable(GL_CULL_FACE);
			glPolygonMode(GL_FRONT,GL_FILL);
		glPopMatrix();
	glEndList();

	ENV_DL = glGenLists(1);
	glNewList(ENV_DL, GL_COMPILE);
		glPushMatrix();
			glTranslatef(0.0,-2.5,0.0);
			glBindTexture(GL_TEXTURE_2D, akers_tex);
			glPushMatrix();
				glTranslatef(-0.5,4,-4);
				glRotatef(90,0,1,0);
				glBegin(GL_QUADS);
					glNormal3f(-1.0,0.0,0.0);
					glTexCoord2d(0.0,0.0);
					glVertex3f(0.0,0.0,0.0);
					glTexCoord2d(1.0,0.0);
					glVertex3f(0.0,0.0,1.0);
					glTexCoord2d(1.0,1.0);
					glVertex3f(0.0,1.0,1.0);
					glTexCoord2d(0.0,1.0);
					glVertex3f(0.0,1.0,0.0);
				glEnd();
			glPopMatrix();
			glBindTexture(GL_TEXTURE_2D, sawin_tex);
			glPushMatrix();
				glTranslatef(0.5,4,4);
				glRotatef(-90,0,1,0);
				glBegin(GL_QUADS);
					glNormal3f(-1.0,0.0,0.0);
					glTexCoord2d(0.0,0.0);
					glVertex3f(0.0,0.0,0.0);
					glTexCoord2d(1.0,0.0);
					glVertex3f(0.0,0.0,1.0);
					glTexCoord2d(1.0,1.0);
					glVertex3f(0.0,1.0,1.0);
					glTexCoord2d(0.0,1.0);
					glVertex3f(0.0,1.0,0.0);
				glEnd();
			glPopMatrix();
			glBindTexture(GL_TEXTURE_2D, floor_tex);
			glFrontFace(GL_CW);
			glBegin(GL_QUADS);
				glNormal3f(0.0,1.0,0.0);
				glTexCoord2d(0.0,0.0);
				glVertex3f(16.0,0.0,-16.0);
				glTexCoord2d(0.0,16.0);
				glVertex3f(16.0,0.0,16.0);
				glTexCoord2d(16.0,16.0);
				glVertex3f(-16.0,0.0,16.0);
				glTexCoord2d(16.0,0.0);
				glVertex3f(-16.0,0.0,-16.0);
			glEnd();
			glFrontFace(GL_CCW);
			setGLMaterial(MAT_WALL);
			glDisable(GL_TEXTURE_2D);
			glBegin(GL_QUADS);
				glNormal3f(-1.0,0.0,0.0);
				glVertex3f(16.0,0.0,-16.0);
				glVertex3f(16.0,0.0,16.0);
				glVertex3f(16.0,8.0,16.0);
				glVertex3f(16.0,8.0,-16.0);

				glNormal3f(1.0,0.0,0.0);
				glVertex3f(-16.0,0.0,16.0);
				glVertex3f(-16.0,0.0,-16.0);
				glVertex3f(-16.0,8.0,-16.0);
				glVertex3f(-16.0,8.0,16.0);

				glNormal3f(0.0,0.0,-1.0);
				glVertex3f(16.0,0.0,16.0);
				glVertex3f(-16.0,0.0,16.0);
				glVertex3f(-16.0,8.0,16.0);
				glVertex3f(16.0,8.0,16.0);

				glNormal3f(0.0,0.0,1.0);
				glVertex3f(-16.0,0.0,-16.0);
				glVertex3f(16.0,0.0,-16.0);
				glVertex3f(16.0,8.0,-16.0);
				glVertex3f(-16.0,8.0,-16.0);
			glEnd();
			glEnable(GL_TEXTURE_2D);
		glPopMatrix();
	glEndList();

	TABLE_DL = glGenLists(1);
	glNewList(TABLE_DL, GL_COMPILE);
		texphongShader->Bind();
		glPushMatrix();
			setGLMaterial(MAT_YELLOW);
			glFrontFace(GL_CW);
			glBegin(GL_QUADS);
				//The table surface (make sure this is at y=0)
				glNormal3f(0.0,1.0,0.0);
				glTexCoord2d(0.0,0.0);
				glVertex3f(tablex/2,0.0,-tablez/2);
				glTexCoord2d(0.0,4.0);
				glVertex3f(tablex/2,0.0,tablez/2);
				glTexCoord2d(1.0,4.0);
				glVertex3f(-tablex/2,0.0,tablez/2);
				glTexCoord2d(1.0,0.0);
				glVertex3f(-tablex/2,0.0,-tablez/2);

				glNormal3f(0.0,0.0,1.0);
				glVertex3f(-tablex/2,0.0,tablez/2);
				glVertex3f(tablex/2,0.0,tablez/2);
				glVertex3f(tablex/2,-0.1,tablez/2);
				glVertex3f(-tablex/2,-0.1,tablez/2);

				glNormal3f(0.0,0.0,-1.0);
				glVertex3f(tablex/2,0.0,-tablez/2);
				glVertex3f(-tablex/2,0.0,-tablez/2);
				glVertex3f(-tablex/2,-0.1,-tablez/2);
				glVertex3f(tablex/2,-0.1,-tablez/2);

				glNormal3f(1.0,0.0,0.0);
				glVertex3f(tablex/2,0.0,tablez/2);
				glVertex3f(tablex/2,0.0,-tablez/2);
				glVertex3f(tablex/2,-0.1,-tablez/2);
				glVertex3f(tablex/2,-0.1,tablez/2);

				glNormal3f(-1.0,0.0,0.0);
				glVertex3f(-tablex/2,0.0,-tablez/2);
				glVertex3f(-tablex/2,0.0,tablez/2);
				glVertex3f(-tablex/2,-0.1,tablez/2);
				glVertex3f(-tablex/2,-0.1,-tablez/2);
			glEnd();
			glFrontFace(GL_CCW);
			setGLMaterial(MAT_RED);
			glTranslatef(tablex/2 - 0.1,-2.51,tablez/2 - 0.1);
			glPushMatrix();
				glRotatef(-90,1.0,0.0,0.0);
				gluCylinder(quadric,0.05,0.075,2.5,16,1);
			glPopMatrix();

			glTranslatef(-(tablex - 0.2),0.0,0.0);
			glPushMatrix();
				glRotatef(-90,1.0,0.0,0.0);
				gluCylinder(quadric,0.05,0.075,2.5,16,1);
			glPopMatrix();

			glTranslatef(0.0,0.0,-(tablez - 0.2));
			glPushMatrix();
				glRotatef(-90,1.0,0.0,0.0);
				gluCylinder(quadric,0.05,0.075,2.5,16,1);
			glPopMatrix();

			glTranslatef((tablex - 0.2),0.0,0.0);
			glPushMatrix();
				glRotatef(-90,1.0,0.0,0.0);
				gluCylinder(quadric,0.05,0.075,2.5,16,1);
			glPopMatrix();
		glPopMatrix();
		setGLMaterial(MAT_WHITE);
		glPushMatrix();
			glTranslatef(-4.0,-0.5,0);
			glRotatef(-90,1,0,0);
			glRotatef(90,0,0,1);
			glScalef(5,5,5);
			drawMesh();
		glPopMatrix();
		texphongShader->UnBind();
	glEndList();
}

void PongGame::drawPongGame(void) {
	//string akers = "oh so close!";
	for(int i=0; i<curMsg.size(); i++){
	glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, curMsg[i]);
	}


	glPushMatrix();
	glRotatef(-180*status[3],0,1,0);
	//cerr << status[3] << endl;
	
	//Draw anything in the surrounding environment
	glEnable(GL_TEXTURE_2D);
	glCallList(ENV_DL);

	//Draw a Table
	glBindTexture(GL_TEXTURE_2D, table_tex);
	glCallList(TABLE_DL);
	glDisable(GL_TEXTURE_2D);
	glPopMatrix();

	//Draw the cups
	for(int i = 0; i < cups.size(); i++){
		if(cups[i]){
			glPushMatrix();
			glTranslatef(cup_pos[i][0], 0, cup_pos[i][1]);
			glCallList(CUP_DL);
			glPopMatrix();
		}
	}

	//Draw our cups
	for(int i = 0; i < cups.size(); i++){
		if(player_cups[i]){
			glPushMatrix();
			glRotatef(180,0,1,0);
			glTranslatef(cup_pos[i][0], 0, cup_pos[i][1]);
			glCallList(CUP_DL);
			glPopMatrix();
		}
	}

	//Draw the Ball
	glPushMatrix();
		setGLMaterial(MAT_WHITE);
		glTranslatef(ballpos[0],ballpos[1],ballpos[2]);
		glutSolidSphere(ball_r,8,8);
		if(phase==2){						// aiming line
			glDisable(GL_LIGHTING);
			setGLMaterial(MAT_GREEN);
			glRotatef(-1*(hAng + 90),0,1,0);
			glRotatef(upAng,1,0,0);
			glBegin(GL_LINES);
				glVertex3f(0,0,0);
				glVertex3f(0,0,.5);
			glEnd();
			glEnable(GL_LIGHTING);
		}
		if(phase==4){
			glColor4f(power, 1-power,0,1.0);

			glDisable(GL_LIGHTING);
			glRotatef(-1*(hAng + 90),0,1,0);
			glRotatef(upAng,1,0,0);
			glBegin(GL_LINES);
				glVertex3f(0,0,0);
				glVertex3f(0,0,.08+power);
			glEnd();
			glEnable(GL_LIGHTING);
		}
	glPopMatrix();
}

void PongGame::idle(void){
	//Find where the ball should be
	//Animate any other moving objects


	if (last_idle== 0.0){
		last_idle= CS315::getTime();
	return;
	}

	double curtime = CS315::getTime();
	double elapsed = curtime - last_idle;
	last_idle = curtime;
	// above currently has no use -- if there will be any animation besides the ball in flight it will require this.


	if(curtime-msgTime>2){
	curMsg="";
	}

	if(phase == 5){  //throw phase
		phase++;
		 vector<double> loc (ballpos, ballpos + sizeof(ballpos) / sizeof(double));
		ball=Ball();
	    power = 3+4*power;
		ball.launchBall(loc,power,upAng,hAng,cups);
		tStart = originalStart =curtime;
	}
	if(phase == 6){							//animation phase
		double throwTimeElapsed = curtime-tStart;
		//cerr << throwTimeElapsed << endl;
		vector<double> tempPos=ball.getPosition(throwTimeElapsed);
		
		ballpos[0]=tempPos[0];
		ballpos[1]=tempPos[1];
		ballpos[2]=tempPos[2];

		//Collision detection, and events
		if(power <= 0.0001){
			//if the ball is very slow, reset
			phase=7;
		}else if(((ballpos[2] > tablez/2  || ballpos[2] < -tablez/2)|| (ballpos[0] > (tablex/2) || ballpos[0] < (-tablex/2))) && ballpos[1] < -1 ){
			//if the ball is out of bounds and below the table, reset
			phase=7;
		} else if((ballpos[1] - ball_r) <= 0 && !((ballpos[2] > tablez/2  || ballpos[2] < -tablez/2)|| (ballpos[0] > (tablex/2) || ballpos[0] < (-tablex/2)))){
			//if the ball should hit the table, rethrow
			power = power*.75;
			

			vector<double> newLoc (ballpos, ballpos + sizeof(ballpos) / sizeof(double));
			vector<double> newAngle = ball.getAngle(throwTimeElapsed);
			//need to return the angle of the power
			//std::cerr << "upAngle = " << upAng << endl;
			
			if(upAng< 0){
				upAng = -upAng;
			}
			upAng +=5;
			ball.launchBall(newLoc,power,upAng,hAng,cups);
			tStart=curtime;
		} else if(ballpos[1] < cup_h + ball_r && onCup(tempPos) && curtime - 0.1 > lastCupHit){
			//if the ball should hit a cup, rethrow (randomly, for now)
			lastCupHit = curtime;
			power = power*.25;
			

			vector<double> newLoc (ballpos, ballpos + sizeof(ballpos) / sizeof(double));
			vector<double> newAngle = ball.getAngle(throwTimeElapsed);
			//need to return the angle of the power
			//std::cerr << "upAngle = " << upAng << endl;
			
			if(upAng< 0){
				upAng = -upAng;
			}
			upAng +=15;
			hAng = hAng+180;
			ball.launchBall(newLoc,power,upAng,hAng,cups);
			cerr << "BOUNCE BOUNCE BOUNCE BOUNCE" << endl;
			tStart=curtime;
		} else if(ballpos[1] < cup_h && inCup(tempPos)){ //replace false with a test for proximity to the center of a cup.  Probably need to move cup positioning to init and keep track of where they end up.
			cerr << "CUP SINK" << endl;
			status[1]=status[1]++;
			status[4]=1;
			phase = 7;
			//if the ball should wind up inside a cup, reset (and count a point)
		}
	}
	if(phase==7){			// reset phase

			if(status[4]==0){
				curMsg=miss[status[2]*5+CS315::RandInt(5)];
				msgTime=curtime;
			}else{
				curMsg=hit[status[2]*5+CS315::RandInt(5)];
				msgTime=curtime;
			}
			status[4]=0;
		if(status[0]==0){
			ballpos[0] = 0;
			ballpos[1] = 1.5;
			ballpos[2] = 4;
			status[0]=1;
			phase=1;
			cerr << "SHOOT AGAIN" << endl;
		}else{
			if(status[1]==2){
			status[0]=0;
			status[1]=0;
			ballpos[0] = 0;
			ballpos[1] = 1.5;
			ballpos[2] = 4;
			phase=1;
			cerr << "BALLS BACK" << endl;
			}
			else if(status[0]==1){
				cerr << "TURN OVER" << endl;
				int Times;

						anim[0]=0;
						phase =8;
			}
			
			}

			
		
		}

	if(phase ==8){  // switches sides with animation
		if(anim[0]<180){
		anim[0]=anim[0]+ 200*elapsed;
		}
		else{
		status[0]=0;  // reset shot count
		status[1]=0;  // reset hitcount
		if(status[3]==0){
			status[3]=1;
		}else{
			status[3]=0;
		}
		if(status[2]==0){
		status[2]=1;

		}else{
		status[2]=0;
		}
		ballpos[0] = 0;
		ballpos[1] = 1.5;
		ballpos[2] = 4;
		phase = 1;
		anim[0]=0;
		switch_cups();
		}
		}
	// We might want/need to detect collisions/scores here

	glutPostRedisplay();
}

bool PongGame::inCup(vector<double> pos){
	for(int i = 0; i < cups.size(); i++){
		if(cups[i] && cup_tr > sqrt((pos[0]-cup_pos[i][0])*(pos[0]-cup_pos[i][0]) + (pos[2]-cup_pos[i][1])*(pos[2]-cup_pos[i][1]))){
			cups[i] = false;
			return true;
		}
	}
	return false;
}

bool PongGame::onCup(vector<double> pos){
	double dist;
	for(int i = 0; i < cups.size(); i++){
		dist = sqrt(((pos[0])-cup_pos[i][0])*((pos[0])-cup_pos[i][0]) + ((pos[2])-cup_pos[i][1])*((pos[2])-cup_pos[i][1]));
		if(cups[i] && dist > cup_tr - ball_r && dist < cup_tr + ball_r){
			return true;
		}
	}
	return false;
}

void PongGame::switch_cups(){
	vector<bool> tmp;
	tmp = cups;
	cups = player_cups;
	player_cups = tmp;
}

void PongGame::keyDown(unsigned char key, int x, int y){


}

void PongGame::mouseClick(int button,int state,int x, int y){
	//std::cout<<"1"<<"\n";
	if(GLUT_LEFT_BUTTON==button){
		//std::cout<<"1"<<"\n";
		if(GLUT_DOWN==state){
			lHold=true;

			if(phase == 1){
				phase = phase +1;
				startX = x;
				startY =y;
				upAng=0;
				hAng=-90;
				return;
			}
			if(phase == 3){
				phase = phase +1;
				startX = x;
				startY =y;
				power=0;
				return;
			}

		}else{
			lHold=false;
			if(phase == 2 || phase == 4){
				phase = phase +1;
				return;
			}
		}
	}else{
		if(GLUT_RIGHT_BUTTON==button){
			if(GLUT_DOWN==state){
				rHold=true;
				lLastX = x;
				lLastY = y;
		}else{
			rHold=false;				
			//viewY=0;					//uncomment these for snapping back
			//viewX=0;
			}
		}

	}
}

void PongGame::mouseDrag(int x,int y){
	if(lHold){
		if(phase == 2){
			//setting angle
			int xdiff = startX-x;
			int ydiff = startY-y;
			upAng=upAng- ydiff*.5;
			hAng=hAng+xdiff*.5;
			startX=x;
			startY=y;
			return;

		}
		if(phase == 4){
			//setting power
			int ydiff = startY-y;
			if(ydiff <0){
				ydiff=ydiff*-1;
			}
			if(ydiff>sy/2){
				power = 1;
			}else{
				power = (double)ydiff/(sy/2);
			}
			std::cerr << "Power = " << power << std::endl;

		}
	}
	if(rHold){
		int ydiff=lLastY-y;
		int xdiff=lLastX-x;

		viewX=viewX-ydiff;
		viewY=viewY-xdiff;

		lLastY=y;
		lLastX=x;
	
	}
}



/************************
 Functions I stole from TOBOR
*************************/

// Resize and initialize the GL window
GLvoid PongGame::reshape(GLint width, GLint height) {
	sx = width;
	sy = height;
  glViewport(0,0,width,height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  // Set up a perspective projection based on the window's aspect ratio
  gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,50.0f);

  // Back to modelview mode to be nice
  glMatrixMode(GL_MODELVIEW);             
  glLoadIdentity();

}

// Initialize OpenGL
void PongGame::initGL() {
  
  // Enable smooth shading
  glShadeModel(GL_SMOOTH);              

  // black Background
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       
  glEnable(GL_DEPTH_TEST);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  glFrontFace(GL_CCW);
  glPolygonMode(GL_FRONT,GL_FILL);
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);

  // Initialize GLEW (OpenGL extension wrangler):
  glewInit();
  phongShader = new STShaderProgram();
  phongShader->LoadVertexShader(phongVertexShaderFilename);
  phongShader->LoadFragmentShader(phongFragmentShaderFilename);
  texphongShader = new STShaderProgram();
  texphongShader->LoadVertexShader(texphongVertexShaderFilename);
  texphongShader->LoadFragmentShader(texphongFragmentShaderFilename);
}


// Turn the light on
void PongGame::lights(void){

  // The position of the light
  GLfloat position[] =  {0.0, 3.0, -2.0, 1.0};

  GLfloat dcolor[] = {.5,.5,.5,1};
  GLfloat acolor[] = {0,0,0,1};
  GLfloat scolor[] = {0.4,0.4,0.4,1};

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  
  glPushMatrix();
  glRotatef(-180*status[3],0,1,0);
  glLightfv(GL_LIGHT0, GL_POSITION, position);
  glLightfv(GL_LIGHT0, GL_AMBIENT, acolor);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, dcolor);
  glLightfv(GL_LIGHT0, GL_SPECULAR, scolor);


  position[3] +=  0.5;
  glEnable(GL_LIGHT1);
  dcolor[0] = 0.6;
  dcolor[1] = 0.4;
  dcolor[2] = 0.1;

  scolor[0] = 0.3;
  scolor[1] = 0.2;
  scolor[2] = 0.05;

  glLightfv(GL_LIGHT1, GL_POSITION, position);
  glLightfv(GL_LIGHT1, GL_AMBIENT, acolor);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, dcolor);
  glLightfv(GL_LIGHT1, GL_SPECULAR, scolor);
  glPopMatrix();

  // Show the user where the light is...
  /*
  glPushMatrix();
  glTranslatef(position[0],position[1],position[2]);
  glDisable(GL_LIGHTING);
  glColor3f(1,1,1);
  glutWireCube(0.1);
  glEnable(GL_LIGHTING);
  glPopMatrix();
  */
}

// Set the current material 
void PongGame::setGLMaterial(MaterialInfo m) {
  float *diffuse = m.getDiffuse();
  glMaterialfv(GL_FRONT, GL_SPECULAR,  m.getSpecular());
  glMaterialf (GL_FRONT, GL_SHININESS, m.getShininess());
  glMaterialfv(GL_FRONT, GL_AMBIENT,   m.getAmbient());
  glMaterialfv(GL_FRONT, GL_DIFFUSE,   m.getDiffuse());
  glColor4f(diffuse[0],diffuse[1],diffuse[2],diffuse[3]);
}

void PongGame::setInnerGLMaterial(MaterialInfo m){
  float *diffuse = m.getDiffuse();
  glMaterialfv(GL_BACK, GL_SPECULAR,  m.getSpecular());
  glMaterialf (GL_BACK, GL_SHININESS, m.getShininess());
  glMaterialfv(GL_BACK, GL_AMBIENT,   m.getAmbient());
  glMaterialfv(GL_BACK, GL_DIFFUSE,   m.getDiffuse());
  glColor4f(diffuse[0],diffuse[1],diffuse[2],diffuse[3]);
}

void PongGame::loadModel(const char* filename) {

  // Load the model
  m.init(filename);

  if (m.getFTotal() == 0) return;

  // Scale it to fit in a small box
  float minx, maxx;
  float miny, maxy;
  float minz, maxz;

  static const float MAX_SIZE = 1.0f;
  
  unsigned int nvertices = m.getVList().size();

  int i;

  for(i=1; i<nvertices; i++) {
    // note: first vertex is "default vertex" - avoid using this as part of size calculations
    Vec3 v = m.getVList()[i];
    if (i == 1 || (v[0] < minx)) minx = v[0];
    if (i == 1 || (v[1] < miny)) miny = v[1];
    if (i == 1 || (v[2] < minz)) minz = v[2];
    if (i == 1 || (v[0] > maxx)) maxx = v[0];
    if (i == 1 || (v[1] > maxy)) maxy = v[1];
    if (i == 1 || (v[2] > maxz)) maxz = v[2];      
  }

  float xcenter = (maxx + minx) / 2.0;
  float ycenter = (maxy + miny) / 2.0;
  float zcenter = (maxz + minz) / 2.0;

  // Find the largest axis
  float span = maxx-minx;
  if (maxy - miny > span) span = maxy-miny;
  if (maxz - minz > span) span = maxz-minz;

  float scale_factor = MAX_SIZE / span;
  
  // Scale and translate each vertex to put the object in
  // a nice box

  for(i=0; i<nvertices; i++) {
    Vec3 v = m.getVList()[i];

    v[0] -= xcenter;
    v[1] -= ycenter;
    v[2] -= zcenter;

    v[0] *= scale_factor;
    v[1] *= scale_factor;
    v[2] *= scale_factor;

    m.getVList()[i] = v;
  }   
}

void PongGame::drawMesh(){
        // Draw our mesh if we have one
  if (m.getFList().size() > 0) {

    float* vertices = (float*)(&(m.getVList()[0]));
    float* normals = (float*)(&(m.getNList()[0]));

    glBegin(GL_TRIANGLES);

        // For each face in the model:
    for(unsigned int i=0; i < m.getFList().size(); i++) {
      Face f = m.getFList()[i];
          Material mat;
          mat = m.getMaterial()[f[0].getMaterialIndex()];
          GLfloat mat_shininess[1];

          // some meshes have a shininess of zero, which causes problems.
          mat_shininess[0] = (mat.Ns > 0) ? mat.Ns : 50.0f;

          glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, mat.Ka);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, mat.Kd);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_SPECULAR, mat.Ks);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

      glNormal3f( normals[3*f[0].getNormalIndex()+0]  , normals[3*f[0].getNormalIndex()+1]  , normals[3*f[0].getNormalIndex()+2] );
      glVertex3f( vertices[3*f[0].getVertexIndex()+0] , vertices[3*f[0].getVertexIndex()+1] , vertices[3*f[0].getVertexIndex()+2] );
      
      mat = m.getMaterial()[f[1].getMaterialIndex()];

          // some meshes have a shininess of zero, which causes problems.
          mat_shininess[0] = (mat.Ns > 0) ? mat.Ns : 50.0f;

          glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, mat.Ka);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, mat.Kd);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_SPECULAR, mat.Ks);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

      glNormal3f( normals[3*f[1].getNormalIndex()+0]  , normals[3*f[1].getNormalIndex()+1]  , normals[3*f[1].getNormalIndex()+2] );
      glVertex3f( vertices[3*f[1].getVertexIndex()+0] , vertices[3*f[1].getVertexIndex()+1] , vertices[3*f[1].getVertexIndex()+2] );
     
          mat = m.getMaterial()[f[2].getMaterialIndex()];

          // some meshes have a shininess of zero, which causes problems.
          mat_shininess[0] = (mat.Ns > 0) ? mat.Ns : 50.0f;

          glMaterialfv ( GL_FRONT_AND_BACK, GL_AMBIENT, mat.Ka);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_DIFFUSE, mat.Kd);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_SPECULAR, mat.Ks);
          glMaterialfv ( GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);

      glNormal3f( normals[3*f[2].getNormalIndex()+0]  , normals[3*f[2].getNormalIndex()+1]  , normals[3*f[2].getNormalIndex()+2] );
      glVertex3f( vertices[3*f[2].getVertexIndex()+0] , vertices[3*f[2].getVertexIndex()+1] , vertices[3*f[2].getVertexIndex()+2] );
      
    }

    glEnd(); 
	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
  }
}



void PongGame::redraw(void) {

  // Clear screen and depth buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
  // Set up the camera in a nice spot:
  // TODO: Implement as gluLookAt or something better than a translate
  glLoadIdentity();
  gluLookAt(0.0,1.5,8.0,0.0,0.25,-3.5,0,1,0);

  glRasterPos3f(-1.5,2.5,2);

  glRotatef(viewX,1,0,0);
  glRotatef(anim[0],0,1,0);
  glRotatef(viewY,0,1,0);
  //glTranslatef(0.0, 2.0, -10.0);

  // Set up lights
  lights();

  // Draw the robot  
  drawPongGame();

  // Show the result to the user
  glutSwapBuffers();
  
}



// Draws an axis vector (line + cone) along the z axis
void PongGame::draw_axis(double length) {
	glPushMatrix();
	glBegin(GL_LINES);
		glVertex3d(0,0,0); 
		glVertex3d(0,0,length); 
	glEnd();
	glTranslated(0,0, length-0.2);
	glutSolidCone(0.04, 0.2, 12, 9);
	glPopMatrix();
}


void PongGame::draw_axes() {

  #define AXIS_LENGTH 3.0

  // draw the axes
  glDisable(GL_LIGHTING);
  glLineWidth(2.0);

  // Draw the z axis
	glColor3d(0, 0, 1.0);
	draw_axis(AXIS_LENGTH);

  // Draw the x and y axes
	glPushMatrix();
	  glRotated(90, 0, 1, 0);
	  glColor3d(1., 0, 0);
	  draw_axis(0.5);
	  glRotated(-90, 1, 0, 0);
	  glColor3d(0, 1., 0);
	  draw_axis(0.5);
	glPopMatrix();
	
  glEnable(GL_LIGHTING);

}


//***************************************************************************
// Constructor
//***************************************************************************
PongGame::PongGame()
{
	sx = 640;
	sy = 480;
}


//***************************************************************************
// DESTRUCTOR
//***************************************************************************
PongGame::~PongGame()
{
  // Nothing to do!
}

//***************************************************************************
// Callback function - is called by GLUT when key is pressed:
//***************************************************************************

void keyDownCallback(unsigned char key, int x, int y)
{
  instance->keyDown(key, x, y);
}

//***************************************************************************
// Callback function - is called by GLUT whenever redraw needs to happen
//***************************************************************************
void redrawCallback()
{
  instance->redraw();
}

//***************************************************************************
// Callback function - is called by GLUT whenever window has been resized
//***************************************************************************
void reshapeCallback(int w, int h) {
  instance->reshape(w, h);
}


//***************************************************************************
// Callback function - is called by GLUT whenever interaction is idle
//***************************************************************************
void idleCallback() {
  instance->idle();
}

void mouseClickCallback(int button,int state, int x, int y){
	instance->mouseClick(button,state,x,y);

}

void mouseDragCallback(int x, int y){
	instance->mouseDrag(x,y);
}

//***************************************************************************
// Main function 
//***************************************************************************

int main(int argc, char** argv) {

  instance = new PongGame();

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
  glutInitWindowSize(640,480);
  glutInitWindowPosition(100,150);
  glutCreateWindow("PongGame");
  glutIdleFunc(idleCallback);
  glutDisplayFunc(redrawCallback);
  glutReshapeFunc(reshapeCallback);
  glutKeyboardFunc(keyDownCallback);

  glutMouseFunc(mouseClickCallback);
  glutMotionFunc(mouseDragCallback);


  instance->initGL();
  instance->initializePongGame();
  glutMainLoop();

  return 0;
}

